package web.service.impls;

import java.lang.reflect.Type;
import java.net.HttpURLConnection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import map.beans.MapEngineRegistrationBean;
import map.beans.SchemaBean;
import map.beans.SchemaBean.SchemaProperty;
import map.beans.ServerInfoBean;
import map.utils.StringUtils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import web.dao.interfaces.EngineRegistrationDao;
import web.service.interfaces.EngineRegistrationServiceInterface;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;

@Repository("engRegService")
public class EngineRegistrationServiceImpl implements EngineRegistrationServiceInterface {
	private static Logger LOGGER = LoggerFactory.getLogger(EngineRegistrationServiceImpl.class);
	public static ServerInfoBean serverInfo = new ServerInfoBean();
	
	@Autowired
	EngineRegistrationDao engineRegDao;

	@Override
	public Long register(MapEngineRegistrationBean engine) {
		try {
			engine.setId(null);
			// if properties defined ? Load properties : empty map is returned						
			engine.setProperties(loadSchemaProperties(engine.getUrl()));		
			
			LOGGER.info("Register URL:\t" + engine.getUrl());
		} catch (Exception e) {
			LOGGER.error(e.getMessage());
		}
		
		return engineRegDao.save(engine);
	}

	@Override
	public Long update(MapEngineRegistrationBean engine) {
		MapEngineRegistrationBean mrb = getMapEngine(engine.getId());
		engine.setId(mrb.getId());
		
		if (engine.getProperties() == null || engine.getProperties().isEmpty() || mrb.getUrl() != engine.getUrl()) {
			// If engine properties do not currently exists 
			// OR are empty
			// OR URL has changed 
			// Attempt to Update the properties at given url
			engine.setProperties(loadSchemaProperties(engine.getUrl()));			
		}
		
		return engineRegDao.update(engine);
	}

	@Override
	public List<MapEngineRegistrationBean> findAll() {
		List<MapEngineRegistrationBean> mapEngines = engineRegDao.findAll();
		try {
			if (mapEngines == null || mapEngines.isEmpty()) {
				registerDefaultEngines();
				mapEngines = engineRegDao.findAll();
			}
		} catch (Exception e) {
			LOGGER.error(e.getMessage());
		}		
		return mapEngines;	
	}

	@Override
	public boolean delete(MapEngineRegistrationBean engine) {
		return engineRegDao.delete(engine);
	}

	@Override
	public MapEngineRegistrationBean getMapEngine(Long id) {
		return engineRegDao.getMapEngine(id);
	}
	
	@Override
	public void setServerInfo(HttpServletRequest req) {
		serverInfo.setContextPath(req.getContextPath());
		serverInfo.setScheme(req.getScheme());
		serverInfo.setHost(req.getServerName());
		serverInfo.setPort(req.getServerPort());
	}
	
	private String getServerUrl() {
		String path = "../";
		if (serverInfo != null) {
			path = serverInfo.getScheme() 
					+ "://" + serverInfo.getHost() 
					+ ":"   + serverInfo.getPort();
		}	
		return path;
	}
	
	private void generateDefaultGoogleEnterprise() {
		MapEngineRegistrationBean googleEnt = new MapEngineRegistrationBean();
		Map<String,String> properties = new HashMap<String,String>();
		properties.put("Base_URL", "http://gedemo.keyhole.com");
		properties.put("Enterprise_Key", "ABCDEFGHIJKLMNOPgoogle.earth.ec.key");
		
		googleEnt.setName("Google Enterprise");
		googleEnt.setUrl(this.getServerUrl() + "/emp-engine-ge/manifest.js");		
		googleEnt.setProperties(properties);
		
		register(googleEnt);
	}
	
	private void generateDefaultWorldwind() {
		MapEngineRegistrationBean wwEngine = new MapEngineRegistrationBean();
		wwEngine.setName("World Wind");
		wwEngine.setUrl(this.getServerUrl() + "/emp-engine-ww/manifest.js");
		Map<String,String> properties = new HashMap<String,String>();
		wwEngine.setProperties(properties);
		
		register(wwEngine);
	}
	
	private void generateDefaultGoogleCommercial() {
		MapEngineRegistrationBean geCommEngine = new MapEngineRegistrationBean();
		geCommEngine.setName("Google Commercial");
		geCommEngine.setUrl(this.getServerUrl() + "/emp-engine-ge/manifest-commercial.js");
		Map<String,String> properties = new HashMap<String,String>();
		geCommEngine.setProperties(properties);
		
		register(geCommEngine);
	}
	
	private void registerDefaultEngines() {
		generateDefaultGoogleCommercial();
		generateDefaultGoogleEnterprise();
		generateDefaultWorldwind();
	}
	
	public Map<String, String> loadSchemaProperties(String url) {
		Map<String, String> props = new HashMap<String, String>();
		// Instantiate gson builder
		Gson gson = new GsonBuilder().create();

		if (StringUtils.urlExist(url) == HttpURLConnection.HTTP_OK) {
			// convert to schema bean
			Type type = new TypeToken<SchemaBean>() {}.getType();
			SchemaBean schema = gson.fromJson(StringUtils.getSchema(url), type);

			// load properties and their default values from the url within the schema.json file, 
			// the location is assumed to be a sibling of the manifest.js file
			if (schema != null) {
				props = (SchemaPropList2Map(schema.getProperties()));
			}
		}

		return props;
	}
	
	private Map<String, String> SchemaPropList2Map(List<SchemaProperty> properties) {
		Map<String, String> map = new HashMap<String,String>();
		if (properties != null) {
			for (SchemaProperty p : properties) {
				map.put(p.getName(), p.getDefaultValue());
			}		
		}
		return map;
	}

	@Override
	public void register(List<MapEngineRegistrationBean> mapEngines) {
		for (MapEngineRegistrationBean mer : mapEngines) {
			register(mer);
		}
	}
}
